package com.dz.finance.wechat.service.impl;

import com.dz.finance.common.exception.ParameterException;
import com.dz.finance.wechat.aes.AesException;
import com.dz.finance.wechat.config.WeChatConfig;
import com.dz.finance.wechat.dto.MpCommonReq;
import com.dz.finance.wechat.entity.MpBaseEventReq;
import com.dz.finance.wechat.service.WeChatMessageStrategy;
import com.dz.finance.wechat.factory.WeChatMessageStrategyFactory;
import com.dz.finance.wechat.service.WechatEventService;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Objects;

/**
 * @author: DengHaoQi
 * @date: 2024/6/6 17:10
 * @description: 微信消息事件发布
 */


@Slf4j
@Service
@RequiredArgsConstructor
public class WechatEventServiceImpl implements WechatEventService {


    private final WeChatConfig weChatConfig;
    private final WeChatMessageStrategyFactory factory;


    /*
     * 接收微信推送事件
     * 参考文档: https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Access_Overview.html
     * */
    @Override
    public String receiveMpEvent(MpCommonReq mpCommonReq, HttpServletRequest request) throws AesException, IOException {
        //  验证消息的确来自微信服务器
        checkSignature(mpCommonReq.getSignature(),
                weChatConfig.getMp().getToken(),
                mpCommonReq.getTimestamp(),
                mpCommonReq.getNonce(),
                null
        );
        // 验证消息的确来自微信服务器（Get请求）
        if (Strings.isBlank(request.getContentType())) {
            // 说明当前仅是为了验证消息的确来自微信服务器.需要原样返回echostr参数内容
            return mpCommonReq.getEchostr();
        }

        // 方法走到这步说明是POST请求，需要处理微信推送的推送XML数据包
        String xmlContent = IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8);
        XmlMapper xmlMapper = new XmlMapper();
        MpBaseEventReq mpBaseEventReq = xmlMapper.readValue(xmlContent, MpBaseEventReq.class);

        WeChatMessageStrategy strategy = factory.getStrategyByMsgType(mpBaseEventReq.getMsgType());
        strategy.publishMessage(strategy.getCurrent(xmlContent));
        return mpCommonReq.getEchostr();
    }


    /**
     * 用SHA1算法生成安全签名
     *
     * @param signature 签名
     * @param token     票据
     * @param timestamp 时间戳
     * @param nonce     随机字符串
     * @param encrypt   密文
     */
    @Override
    public void checkSignature(String signature, String token, String timestamp, String nonce, String encrypt) throws AesException {
        try {

            if (Strings.isBlank(signature) || Strings.isBlank(token)
                    || Strings.isBlank(timestamp) || Strings.isBlank(nonce)) {
                throw new ParameterException("签名参数非法");
            }
            // 将token、timestamp、nonce三个参数进行字典序排序
            String[] array = null;
            if (Strings.isBlank(encrypt)) {
                array = new String[]{token, timestamp, nonce};
            } else {
                array = new String[]{token, timestamp, nonce, encrypt};
            }
            StringBuffer sb = new StringBuffer();
            // 字符串排序
            Arrays.sort(array);
            for (String s : array) {
                sb.append(s);
            }
            String str = sb.toString();
            // 将三个参数字符串拼接成一个字符串（str）进行sha1加密
            // SHA1签名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (byte b : digest) {
                shaHex = Integer.toHexString(b & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }

            // 加密后的字符串可与signature对比，标识该请求来源于微信
            log.info("wx_signature:{},my_signature：{}", signature, hexstr);
            if (!Objects.equals(signature, hexstr.toString())) {
                throw new AesException(AesException.ValidateSignatureError);
            }
        } catch (NoSuchAlgorithmException exception) {
            exception.printStackTrace();
            throw new AesException(AesException.ComputeSignatureError);
        }
    }
}
